package com.ayz.gateway.assist.listener;


import com.ayz.gateway.common.notify.ApplicationListener;
import com.ayz.gateway.core.context.config.Configuration;
import com.ayz.gateway.core.rpc.MappedMethod;
import com.ayz.gateway.core.rpc.config.DubboConfiguration;
import com.ayz.gateway.core.rpc.dubbo.DubboGenericReferenceRegistry;
import com.ayz.gateway.core.rpc.utils.NamingGenerator;
import com.ayz.gateway.sdk.gatawayService.notify.event.AllocationApiDataChangeEvent;
import lombok.Getter;
import lombok.Setter;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.rpc.service.GenericService;


import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ClassName AllocationApiDataChangeListener
 * @Description TODO: AllocationApiDataChange监听器
 * 当前网关注册中心分配给当前网关的API信息发生变更后，拉取或推送给网关服务
 * 然后网关服务需要将这些API变更 注册到本地Configuration
 * @Author 快乐的星球
 * @Date 2023/5/31 21:36
 * @Version 1.0
 **/
@Getter
@Setter
public class AllocationApiDataChangeListener implements ApplicationListener<AllocationApiDataChangeEvent> {
    /**
     * 网关全局上下文的配置类
     */
    private Configuration configuration;

    public AllocationApiDataChangeListener(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public void onApplicationEvent(AllocationApiDataChangeEvent event) {
        AllocationApiDataChangeEvent.AllocationApiDataChangeInfo apiDataChangeInfo=
                (AllocationApiDataChangeEvent.AllocationApiDataChangeInfo) event.getSource();
        if(configuration instanceof DubboConfiguration){
            registerDubboConfiguration(apiDataChangeInfo);
        }
    }
    private synchronized void registerDubboConfiguration(AllocationApiDataChangeEvent.AllocationApiDataChangeInfo apiDataChangeInfo) {
        DubboConfiguration dubboConfiguration = (DubboConfiguration) configuration;
        ConcurrentHashMap<String, RegistryConfig> registryConfigMap = dubboConfiguration.getRegistryConfigMap();
        ConcurrentHashMap<String, ReferenceConfig<GenericService>> referenceConfigMap = dubboConfiguration.getReferenceConfigMap();
        ConcurrentHashMap<String, MappedMethod> uriResourceMappingRpcMethod = dubboConfiguration.getUriResourceMappingRpcMethod();
        DubboGenericReferenceRegistry dubboGenericReferenceRegistry =(DubboGenericReferenceRegistry) dubboConfiguration.getGenericReferenceRegistry();
        //1:处理新增的API接口
        List<AllocationApiDataChangeEvent.ApiInfo> addApiInfoList = apiDataChangeInfo.getAddApiInfoList();
        if (addApiInfoList != null && !addApiInfoList.isEmpty()) {
            addApiInfoList.forEach((apiInfo -> {
                //1.1: 注册registryConfig
                String applicationName = apiInfo.getApplicationName();
                String registryAddress = apiInfo.getRegistryAddress();
                String interfaceName = apiInfo.getInterfaceName();
                boolean async = apiInfo.isAsync();
                long timeoutMills = apiInfo.getTimeout();
                if (!registryConfigMap.containsKey(applicationName)) {
                    RegistryConfig registryConfig = new RegistryConfig();
                    registryConfig.setAddress(registryAddress);
                    registryConfigMap.put(applicationName, registryConfig);
                }

                if (!referenceConfigMap.containsKey(interfaceName)) {
                    ReferenceConfig<GenericService> referenceConfig = new ReferenceConfig<>();
                    referenceConfig.setInterface(interfaceName);
                    referenceConfig.setGeneric("true");
                    referenceConfig.setAsync(async);
                    referenceConfig.setTimeout((int) timeoutMills);
                    referenceConfigMap.put(interfaceName, referenceConfig);
                }

            }));
        }
        //2:处理API接口下新增的RPC方法
        List<AllocationApiDataChangeEvent.MethodInfo> addMethodInfoList = apiDataChangeInfo.getAddMethodInfoList();
        if (addMethodInfoList != null && !addMethodInfoList.isEmpty()) {
            addMethodInfoList.forEach(methodInfo -> {
                String applicationName = methodInfo.getApplicationName();
                String interfaceName = methodInfo.getInterfaceName();
                String methodName = methodInfo.getMethodName();
                String httpUri = methodInfo.getHttpUri();
                boolean sync = methodInfo.isSync();
                String parameterTypeName = methodInfo.getParameterTypeName();
                //TODO:两个问题 完成RPC方法的注册 和 HTTP->RPC映射资源注册
                dubboConfiguration.registryGenericReference(applicationName, interfaceName, methodName);

                MappedMethod mappedMethod = new MappedMethod(httpUri, applicationName, interfaceName, methodName,
                        sync, parameterTypeName);
                mappedMethod.setAuth(methodInfo.isAuth());
                dubboConfiguration.registerURIMapped(httpUri, mappedMethod);
            });
        }
        //3:处理删除的API接口
        List<AllocationApiDataChangeEvent.ApiInfo> deleteApiInfoList = apiDataChangeInfo.getDeleteApiInfoList();
        if(deleteApiInfoList!=null && !deleteApiInfoList.isEmpty()){
            for (AllocationApiDataChangeEvent.ApiInfo apiInfo : deleteApiInfoList) {
                String interfaceName = apiInfo.getInterfaceName();
                String applicationName = apiInfo.getApplicationName();
                referenceConfigMap.remove(interfaceName);

                HashMap<String, GenericService> genericServiceCache = dubboGenericReferenceRegistry.getGenericServiceCache();
                String cacheKey=applicationName+"@"+interfaceName;
                genericServiceCache.remove(cacheKey);
            }
        }
        //4:处理删除的RPC方法
        List<AllocationApiDataChangeEvent.MethodInfo> deleteMethodInfoList = apiDataChangeInfo.getDeleteMethodInfoList();
        if(deleteApiInfoList!=null && !deleteMethodInfoList.isEmpty()){
            for (AllocationApiDataChangeEvent.MethodInfo methodInfo : deleteMethodInfoList) {
                /**
                 * TODO: 删除 HTTP->RPC方法的映射关系
                 *       RPC泛化调用工厂的映射关系
                 */
                String applicationName = methodInfo.getApplicationName();
                String interfaceName = methodInfo.getInterfaceName();
                String methodName = methodInfo.getMethodName();
                String httpUri = methodInfo.getHttpUri();
                uriResourceMappingRpcMethod.remove(httpUri);
                dubboGenericReferenceRegistry.getKnownGenericReference().remove(NamingGenerator.generateRpcResourceName(applicationName,interfaceName,methodName));
            }
        }
    }
}
